package com.hup.utils.swing.customView;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.swing.JComponent;
import javax.swing.JPanel;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

/**
 * <pre>
 * 简陋的ListView,利用嵌套多层JPanel(GridBagLayout)实现功能
 * 实现:
 * .竖直布局的FlowLayer
 * .铺满子控件宽度
 * .带滚动条
 * .不像GridLayer那样同一子元素高度
 * 方案说明:
 * .难点/关注点:插入/删除子控件后,需要让index保持一致[root中的child列表,gbc中的yIndex]
 * .目的是使用尽量少且简单的代码实现功能
 * .使用GridBagLayout,嵌套多层JPanel,优点:
 *      .不用手动配置/计算子元素的PreferredSize/minSize
 *      .每次修改组件时不用管spacer
 *      .避免了滚动条的展示和隐藏时,要加入一堆调整子元素宽度的代码
 * .缺点:嵌套了多层JPanel,会有类似"安卓过度绘制"的问题,不深究
 *
 * @author hugan
 * @apiNote 网上很多实现方式, 有原始代码的, swingx.VerticalLayout
 * @date 2022/1/11
 */
@Slf4j
public class HListView<E extends JComponent> extends HScrollPanel {

    /**
     * 存放用户控件的panel,不是realRoot
     */
    @Getter
    private final JPanel root;

    public HListView() {
        JPanel realRoot = new JPanel(new GridBagLayout());
        setViewportView(realRoot);
        //
        root = new JPanel(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        realRoot.add(root, gbc);
        //自动拉高的底部spacer
        JPanel spacer = new JPanel();
        spacer.setOpaque(false);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.VERTICAL;
        realRoot.add(spacer, gbc);
    }

    public void setChildren(Iterable<E> children) {
        root.removeAll();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        int i = 0;
        for (E child : children) {
            gbc.gridy = i;
            root.add(child, gbc);
            i++;
        }
        revalidate();
        repaint();
    }

    /**
     * 添加控件到尾部
     */
    public void addChild(E child) {
        int count = root.getComponentCount();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = count;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        root.add(child, gbc);
        revalidate();
        repaint();
    }

    /**
     * 插入控件
     */
    public void addChild(int index, E child) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = index;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        root.add(child, gbc, index);
        fixGridBagConstraints(index + 1);
    }

    public int getChildCount() {
        return root.getComponentCount();
    }

    @SuppressWarnings("unchecked")
    public E getChild(int index) {
        return (E) root.getComponent(index);
    }

    public int indexOfChild(JComponent child) {
        int size = root.getComponentCount();
        for (int i = 0; i < size; i++) {
            Component c = root.getComponent(i);
            if (c == child) return i;
        }
        return -1;
    }

    public void removeAllChild() {
        root.removeAll();
        revalidate();
        repaint();
    }

    public void removeChild(JComponent child) {
        int index = indexOfChild(child);
        if (index == -1) throw new ArrayIndexOutOfBoundsException("No such child: ");
        root.remove(index);
        fixGridBagConstraints(index);
    }

    /**
     * 插入/删除子控件后,修正 startIndex 后的GridBagConstraints
     */
    private void fixGridBagConstraints(int startIndex) {
        //重置后面的GridBagConstraints
        GridBagLayout layout = (GridBagLayout) root.getLayout();
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.BOTH;

        int size = root.getComponentCount();
        //log.info("修正index:[{},{})", startIndex, size);
        for (int i = startIndex; i < size; i++) {
            Component component = root.getComponent(i);
            gbc.gridy = i;
            layout.setConstraints(component, gbc);
        }
        revalidate();
        repaint();
    }

}
